package com.krolik22.nio.learn;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Evgeny Smirnov 29-Dec-2012
 *
 */
public class NIOTest {
	
	private static final Logger LOG = LoggerFactory.getLogger(NIOTest.class);
	
	private static final int BUFFER_LENGTH = 8192;
	
	public static void main(final String[] argv) throws IOException {
		if (argv.length < 2) {
			System.out.println("Usage: " + NIOTest.class.getName() + " <file_name>");
			System.exit(1);
		}
		
		final String source = argv[0];
		final String destination = source + ".bin";
		
		NIOTest smasher = new NIOTest();
		smasher.processFile(source, destination, Byte.parseByte(argv[1]));
	}

	protected void processFile(final String source, final String destination, final byte mask) {
		LOG.info(String.format("Copying %s to %s with 0x%X mask.", source, destination, mask));
		
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			// Create an instance of FileInputStream to read the source
			// content
			fis = new FileInputStream(source);
			// Create an instance of FileOutputStream to write the data
			// into destination file
			fos = new FileOutputStream(destination);

			// Get the FileChannel of FileInputStream instance
			FileChannel fci = fis.getChannel();
			// Get the FileChannel of FileOutputStream instance
			FileChannel fco = fos.getChannel();

			// Create a buffer with 1024 size for buffering data
			// while copying from source file to destination file.
			// To create the buffer here we used a static method
			// ByteBuffer.allocate()
			LOG.info("Allocating {} byte(s) for buffering", BUFFER_LENGTH);
			ByteBuffer buffer = ByteBuffer.allocate(BUFFER_LENGTH);

			// Here we start to read the source file and write it
			// to the destionation file. We repeat this process
			// until the read method of input stream channel return
			// nothing (-1).
			while(true)
			{
				// read a block of data and put it in the buffer
				int read = fci.read(buffer);

				// did we reach the end of the channel? if yes
				// jump out the while-loop
				if (read == -1)
					break;

				// flip the buffer
				buffer.flip();
				
				processBuffer(buffer, mask);

				// write to the destination channel
				fco.write(buffer);

				// clear the buffer and user it for the next read
				// process
				buffer.clear();
			}

		} catch (IOException e) {
			LOG.error("Unexpected error. Problem : " + e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(fos);
			IOUtils.closeQuietly(fis);
		}
	}

	private void processBuffer(final ByteBuffer buffer, final byte mask) {
		byte value;
//		byte result;
		int limit = buffer.limit();
		for (int pos = 0; pos < limit; pos++) {
			value = buffer.get(pos);
			buffer.put(pos, value/*result*/);
		}
	}
}
